package mvc.core;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import mvc.core.annotation.Batis;
import mvc.core.annotation.Controller;
import mvc.core.annotation.DELETE;
import mvc.core.annotation.GET;
import mvc.core.annotation.INPUT;
import mvc.core.annotation.Impl;
import mvc.core.annotation.Inject;
import mvc.core.annotation.Mapping;
import mvc.core.annotation.POST;
import mvc.core.annotation.PUT;
import mvc.core.annotation.Service;
import mvc.core.config.ConfigContext;
import mvc.core.log.Logger;
import mvc.core.util.Util;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
 * Bean工厂
 * 
 * @author liuzy
 * @version 2015年5月13日
 */
public class BeanFactory {

	private static Logger logger = Logger.getLogger(BeanFactory.class);

	protected static Set<Class<?>> allClasses = null;

	/** Bean仓库 */
	private static Map<Class<?>, Object> beanMap = new HashMap<Class<?>, Object>();

	/** Mapping仓库 */
	private static Map<String, ActionMapping> actionMappingMap = new HashMap<String, ActionMapping>();

	/**
	 * 读xml加载并注入Bean
	 * 
	 * @param fileName
	 */
	protected static void loadBeanByXml(String fileName) {
		logger.debug("读" + fileName + "开始...");
		try {
			// 读取指定的配置文件
			SAXReader reader = new SAXReader();
			ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
			// 从class目录下获取指定的xml文件
			InputStream ins = classLoader.getResourceAsStream(fileName);
			Document doc = reader.read(ins);
			Element _beans = doc.getRootElement();
			Element _bean;
			// 遍历bean
			for (Iterator<?> _beans_i = _beans.elementIterator("bean"); _beans_i.hasNext();) {
				_bean = (Element) _beans_i.next();
				// 获取bean的属性id和class
				// String id = _bean.attribute("id").getText();
				String clazzName = _bean.attribute("class").getText();
				Class<?> beanClass = Class.forName(clazzName);
				Field[] beanFields = beanClass.getDeclaredFields();
				Object obj = beanClass.newInstance();
				// 遍历该bean的property属性
				for (Iterator<?> _bean_i = _bean.elementIterator("property"); _bean_i.hasNext();) {
					Element _property = (Element) _bean_i.next();
					// 获取该property的name和value
					String name = _property.attribute("name").getText().trim();
					String value = _property.attribute("value").getText().trim();
					if (value == null) {
						value = ((Element) _property.elementIterator("value").next()).getText().trim();
					}
					if (value != null && value.startsWith("${") && value.endsWith("}")) {
						value = ConfigContext.get(value.substring(2, value.length() - 1));
					}
					// 把属性值set到对象
					for (Field field : beanFields) {
						if (name.equals(field.getName())) {
							field.setAccessible(true);// 必须使该字段可访问
							field.set(obj, value);
							break;
						}
					}
				}
				logger.debug("载入Bean:" + clazzName);
				beanMap.put(beanClass, obj);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		logger.debug("读" + fileName + "结束");
	}

	/**
	 * 扫描注解加载Bean
	 */
	protected static void loadBeanByAnnotations() {
		logger.debug("扫描注解加载Bean开始...");
		try {
			allClasses = ClassScanner.scanner();
			for (Class<?> cls : allClasses) {
				if (cls.isAnnotationPresent(Controller.class)) {
					logger.debug("载入ActionBean:" + cls.getName());
					beanMap.put(cls, (Object) cls.newInstance());
					String actionPath = "/";
					if (cls.isAnnotationPresent(Mapping.class)) {
						actionPath += cls.getAnnotation(Mapping.class).value();
					}
					Method[] methods = cls.getMethods();
					for (Method method : methods) {
						if (method.isAnnotationPresent(Mapping.class)) {
							String path = Util.reSlash(actionPath + "/" + method.getAnnotation(Mapping.class).value());
							List<String> types = new ArrayList<>();
							if (method.isAnnotationPresent(GET.class))
								types.add("GET");
							else if (method.isAnnotationPresent(POST.class))
								types.add("POST");
							else if (method.isAnnotationPresent(PUT.class))
								types.add("PUT");
							else if (method.isAnnotationPresent(DELETE.class))
								types.add("DELETE");
							else if (method.isAnnotationPresent(INPUT.class))
								types.add("INPUT");
							else
								types.addAll(Arrays.asList("GET", "POST", "PUT", "DELETE", "INPUT"));
							actionMappingMap.put(path, new ActionMapping(path, types, cls, method));
							logger.debug("添加映射:path=" + path + ", type=" + types + " action=" + cls.getName() + ", method=" + method.getName());
						}
					}
				} else if (cls.isAnnotationPresent(Service.class)) {
					logger.debug("载入ServiceBean:" + cls.getName());
					beanMap.put(cls, (Object) cls.newInstance());
				} else if (cls.isAnnotationPresent(Batis.class)) {
					logger.debug("载入BatisBean:" + cls.getName());
					beanMap.put(cls, (Object) new MapperProxyFactory<>(cls).newInstance());
				}
			}
		} catch (InstantiationException e1) {
			e1.printStackTrace();
		} catch (IllegalAccessException e2) {
			e2.printStackTrace();
		}
		logger.debug("扫描注解加载结束");
	}

	/**
	 * 扫描注解注入Bean属性
	 */
	protected static void iocBean() {
		logger.debug("注入Bean属性开始...");
		try {
			for (Map.Entry<Class<?>, Object> beanEntry : beanMap.entrySet()) {
				// 获取 Bean 类与 Bean 实例
				Class<?> beanClass = beanEntry.getKey();
				Object beanInstance = beanEntry.getValue();
				// 获取 Bean 类中所有的字段（不包括父类中的方法）
				Field[] beanFields = beanClass.getDeclaredFields();
				if (beanFields != null) {
					// 遍历所有的 Bean 字段
					for (Field beanField : beanFields) {
						// 判断当前 Bean 字段是否带有 @Inject 注解
						if (beanField.isAnnotationPresent(Inject.class)) {
							// 获取 Bean 字段对应的接口
							Class<?> interfaceClass = beanField.getType();
							// 判断接口上是否标注了 @Impl 注解
							Class<?> implClass = null;
							if (interfaceClass.isAnnotationPresent(Impl.class)) {
								// 获取强制指定的实现类
								implClass = interfaceClass.getAnnotation(Impl.class).value();
							} else if (interfaceClass.isAnnotationPresent(Service.class)) {
								implClass = interfaceClass;
							} else if (interfaceClass.isAnnotationPresent(Batis.class)) {
								implClass = interfaceClass;
							} else {
								// 寻找接口所有的实现类
								List<Class<?>> implClassList = new ArrayList<>();
								for (Class<?> cls : allClasses) {
									if (Util.isImpl(cls, interfaceClass))
										implClassList.add(cls);
								}
								if (implClassList.size() > 0) {
									// 获取第一个实现类
									implClass = implClassList.get(0);
								} else
									logger.error("注入" + beanClass.getName() + "的属性" + beanField.getName() + "失败,未找到实现类");
							}
							// 若存在实现类，则执行以下代码
							if (implClass != null) {
								// 从 Bean Map 中获取该实现类对应的实现类实例
								Object implInstance = beanMap.get(implClass);
								// 设置该 Bean 字段的值
								if (implInstance != null) {
									beanField.setAccessible(true); // 取消类型安全检测（可提高反射性能）
									beanField.set(beanInstance, implInstance);
									logger.debug("注入" + beanClass.getName() + "的属性" + beanField.getName() + "成功");
								}
							} else
								logger.error("注入" + beanClass.getName() + "的属性" + beanField.getName() + "失败");
						}
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		logger.debug("注入Bean属性结束");
	}

	@SuppressWarnings("unchecked")
	public static <T> T getBean(Class<T> cls) {
		return (T) beanMap.get(cls);
	}

	protected static ActionMapping getActionMapping(String target) {
		if (Util.isEmpty(target))
			return null;
		for (String key : actionMappingMap.keySet()) {
			ActionMapping actionMapping = actionMappingMap.get(key);
			if (target.equals(actionMapping.getMappingPath())) {
				return actionMapping;
			}
		}
		return null;
	}

	protected static void stop() {
		actionMappingMap.clear();
		beanMap.clear();
	}

}